
#include "../net/Broker4.h"
#include "../iface/APRInterface.h"
#include "Hash.h"
#include "SerialBlockSink.h"

SerialBlockSink::SerialBlockSink(const char *directorypath, apr_pool_t *imp)
{
   apr_status_t status;
   status = apr_pool_create(&mp, imp);
   CheckAPRError(status);

#if defined(__GNUC__) && (defined(__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__NEXT_CPP__) || defined(__MACOS_CLASSIC__))
   const char *argv_for_pipe[] = { "/usr/bin/tar", "xf", "-", "-C", NULL, NULL };
#else
   const char *argv_for_pipe[] = { "/bin/tar", "xf", "-", "-C", NULL, ".", NULL };
#endif
   if (argv_for_pipe[4] != NULL)
      errprint("path not null before specification!");
   directory_name = (char *)apr_palloc(mp, sizeof(char)*(strlen(directorypath)+1));
   strcpy(directory_name, directorypath);
   argv_for_pipe[4] = directory_name;

   status = apr_procattr_create(&pattr, mp);
   CheckAPRError(status);
   status = apr_procattr_io_set(pattr, APR_FULL_BLOCK, APR_NO_PIPE, APR_NO_PIPE);
   CheckAPRError(status);
   status = apr_procattr_cmdtype_set(pattr, APR_PROGRAM_ENV);
   CheckAPRError(status);
   status = apr_proc_create(&tproc, argv_for_pipe[0], (const char* const*)argv_for_pipe, NULL, pattr, mp);
   CheckAPRError(status);

   apr_sleep(1000000);
   int result;
   apr_exit_why_e why;
   status = apr_proc_wait(&tproc, NULL, NULL, APR_NOWAIT);
   if (status != APR_CHILD_NOTDONE)
   {
      errprint("problem starting child process");
      if (directory_name) free(directory_name);
      directory_name = NULL;
   }
}

SerialBlockSink::~SerialBlockSink()
{
   apr_status_t status;
   int result;
   apr_exit_why_e why;
   status = apr_file_close(tproc.in);
   CheckAPRError(status);
   apr_sleep(1000000);
   if (directory_name)
   {
      status = apr_proc_wait(&tproc, &result, &why, APR_NOWAIT);
      if (status == APR_CHILD_NOTDONE)
      {
         // Watch out, the kill can come into effect too early and chop of the final data block!
#ifndef WAITFORPROC
         status = apr_proc_kill(&tproc, SIGTERM);
         CheckAPRError(status);
#endif
         status = apr_proc_wait(&tproc, &result, &why, APR_WAIT);
         if (status == APR_CHILD_NOTDONE)
            errprint("child process is still running");
      }
      if (result != 0)
         errprint("exit code from '%s' non-zero (%d)", "/bin/tar", result);
   }
   apr_pool_destroy(mp);
}

int SerialBlockSink::PutNextBlock(char *realbuffer, int *realsize)
{
   if (directory_name == NULL) { errprint("no directory specified"); return -1; }
   if (realsize == 0) { errprint("no data in block"); return -1; }
   apr_size_t asize = *realsize;

   apr_status_t status;
   status = apr_file_write_full(tproc.in, realbuffer, asize, &asize);
   if (!(APR_STATUS_IS_EOF(status))) CheckAPRError(status);
   if (asize == 0) { errprint("no data written"); return -1; }
   int befsize = *realsize;
   (*realsize) = asize;
   return (befsize != asize);
}

int SerialBlockSink::PutNextBlockDecrypted(const char *cname, KeyPair *kp, const char *realbuffer, int *realsize, int *cesize, char **hash)
{
   if ((directory_name == NULL) || (kp == NULL))
   {
      if (!directory_name) errprint("no directory specified");
      if (!kp) errprint("cannot decrypt with no key");
      *cesize = 0;
      *realsize = 0;
      return -1;
   }
   std::vector<char> *dec_block_vec = Broker4::SimpleCryptDataChunk(cname, NULL, kp, NULL, realbuffer, *cesize, 1, 1, mp, NULL);
   if ((dec_block_vec == NULL) || (dec_block_vec->size() == 0)) { errprint("no data decrypted"); *realsize = 0; return -1; }
   int decsize = dec_block_vec->size();
   if (hash)
      *hash = Hash::getSHA1Hash(&(dec_block_vec->at(0)), decsize);
   if (realsize) *realsize = decsize;
   PutNextBlock(&(dec_block_vec->at(0)), &decsize);
   return (decsize == (*realsize));
}

int SerialBlockSink::PutNextBlockDecrypted(const char *decwith, KeyDB *kdb, const char *realbuffer, int *realsize, int *cesize, char **hash)
{
   KeyPair *kp = kdb->GetPrivateKeyByName(decwith);
   int retval = PutNextBlockDecrypted(decwith, kp, realbuffer, realsize, cesize, hash);
   delete kp;
   return retval;
}
